home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 23 code / Multipane Dialogs Code / MPDialogs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-13  |  35.6 KB  |  1,344 lines  |  [TEXT/MMCC]

  1. #include "MPDialogs.h"
  2. #include "Utilities.h"
  3. #include "ListControl.h"
  4. #include <Palettes.h>
  5. #include <Icons.h>
  6.  
  7. /********************************************************************************
  8.  *
  9.  * Main Multi-Pane Dialog Routines
  10.  *
  11.  ********************************************************************************/
  12.  
  13. DialogPtr OpenMPDialog(short resID, DefActionUPP defAction, ClickActionUPP clickProc,
  14.     EditActionUPP editAction, GroupActionUPP groupAction, Handle *theData)
  15. {
  16.     DialogPtr dlog;
  17.     GrafPtr oldPort;
  18.     MPDHdl dataH;
  19.     OSErr err = noErr;
  20.     short i, num, *dPtr, iType;
  21.     Str255 theStr;
  22.     Handle iHandle, dHandle;
  23.     Rect iRect;
  24.     ListHandle theList;
  25.     Cell theCell;
  26.     
  27.     // One-time initialization of utility routines
  28.     if (!gInitMPDUtils) InitMPDUtils();
  29.     
  30.     // Open the MPD and allocate the MPD's MPDHdl
  31.     if (!(dlog = MyGetCenteredDialog(resID, nil, nil, (WindowPtr) -1L))) {
  32.         return NULL;
  33.     }
  34.     if (!(dataH = (MPDHdl) NewHandle(sizeof(MPDRec)))) {
  35.         DisposeDialog(dlog);
  36.         return NULL;
  37.     }
  38.     MoveHHi((Handle) dataH);
  39.     SetWRefCon(dlog, (long) dataH);
  40.     (*dataH)->radio = NULL;
  41.     (*dataH)->currentPane = 1;
  42.     // Set up Action Procedures as requested
  43.     InstallAction(kItemAction, dlog, (UniversalProcPtr) clickProc);
  44.     InstallAction(kEditAction, dlog, (UniversalProcPtr) editAction);
  45.     InstallAction(kDefAction, dlog, (UniversalProcPtr) defAction);
  46.     InstallAction(kGroupAction, dlog, (UniversalProcPtr) groupAction);
  47.  
  48.     // Run though the dialog items and set user items to be the gray box
  49.     num = CountDITL(dlog);
  50.     (*dataH)->baseItems = num + 1;
  51.     for(i = kIconBox + 1; i <= num; i++) {
  52.         GetDItem(dlog, i, &iType, &iHandle, &iRect);
  53.         if ((iType & 0x7f) == userItem)
  54.             SetItemToGray(dlog, i);
  55.     }
  56.  
  57.     // Get ready for drawing
  58.     GetPort(&oldPort);
  59.     SetPort(dlog);
  60.     
  61.     // Fetch the DTL# resource, die if we can't
  62.     if (!(dHandle = GetResource(kDTL, resID))) goto die3;
  63.  
  64.     HLock(dHandle);
  65.     
  66.     // Read in the pane IDs
  67.     dPtr = (short *) *dHandle;
  68.     num = (*dataH)->numPanes = *dPtr++;
  69.     (*dataH)->paneIDs = (short *) NewPtr(num * sizeof(short));
  70.     for(i=0; i<num; i++) {
  71.         (*dataH)->paneIDs[i] = *dPtr++;
  72.         dPtr += (*(char *) dPtr + sizeof(short)) >> 1;
  73.     }
  74.     (*dataH)->IconHandles = (Handle *) NewPtr(num * sizeof(Handle));
  75.  
  76.     // Set up the icon list control
  77.     GetDItem(dlog, kIconBox, &iType, &iHandle, &iRect);
  78.     if (!(theList = CLNew(rListCtl, true, &iRect, 0, 1, 60, 64, rIconDef, dlog,
  79.             clVScroll | clActive | clKeyPos | clDrawIt))) goto die1;
  80.     (*theList)->selFlags |= lOnlyOne;
  81.     (*dataH)->theList = theList;
  82.     LAddRow(num, 0, theList);
  83.     
  84.     // Add the icons and label to the list
  85.     theCell.h = 0;
  86.     dPtr = ((short *) *dHandle) + 1;
  87.     for (theCell.v = 0; theCell.v < num; theCell.v++) {
  88.         dPtr++;
  89.         Pstrcpy(theStr, (ConstStr255Param) dPtr);
  90.         dPtr += (*(char *) dPtr + sizeof(short)) >> 1;
  91.         GetIconSuite(&(*dataH)->IconHandles[theCell.v], (*dataH)->paneIDs[theCell.v], svAllAvailableData);
  92.         LSetCell(&(*dataH)->IconHandles[theCell.v], sizeof(Handle), theCell, theList);
  93.         LAddToCell(theStr+1, theStr[0], theCell, theList);
  94.         
  95.         CreateRadioGroups(dlog, theCell.v);
  96.     }
  97.     ReleaseResource(dHandle);
  98.  
  99.     // Allocate the MPD and temporary data handles
  100.     if (!*theData) {
  101.         (*dataH)->theData = MPDTemplateFromDITL(dataH);
  102.         *theData = (*dataH)->theData;
  103.         *(long *) **theData = (*dataH)->currentPane;
  104.     } else {
  105.         (*dataH)->theData = *theData;
  106.         (*dataH)->currentPane = *(long *) **theData;
  107.     }
  108.     (*dataH)->tmpData = NewHandle(GetHandleSize((*dataH)->theData));
  109.     if (!(*dataH)->theData || !(*dataH)->tmpData) goto die2;
  110.  
  111.     // Display the first pane
  112.     InstallDITL(dlog, (*dataH)->currentPane);
  113.     HandleMPDAction(dlog, kR2TAction, 0);
  114.     HandleMPDAction(dlog, kT2PAction, (*dataH)->currentPane);
  115.     
  116.     if (theList) {
  117.         LDoDraw(true, theList);
  118.         SetPt(&theCell, 0, (*dataH)->currentPane - 1);
  119.         LSetSelect(true, theCell, theList);
  120.     }
  121.     ShowWindow(dlog);
  122.     SetPort(oldPort);
  123.  
  124.     return dlog;
  125.     
  126.     die1:
  127.         DisposePtr((Ptr) (*dataH)->IconHandles);
  128.     die2:
  129.         DisposePtr((Ptr) (*dataH)->paneIDs);
  130.         DisposeHandle(dHandle);
  131.     die3:
  132.         DisposeHandle((Handle) dataH);
  133.         DisposDialog(dlog);
  134.         return NULL;
  135. }
  136.  
  137. void CloseMPDialog(DialogPtr *dlog)
  138. {
  139.     MPDHdl dataH;
  140.     short i;
  141.     RadioGroupPtr next, group;
  142.     
  143.     dataH = (MPDHdl) GetWRefCon(*dlog);
  144.     // Preserve last open pane
  145.     *(long *) *(*dataH)->theData = (*dataH)->currentPane;
  146.     // Dispose of the icon suites
  147.     for(i = 0; i < (*dataH)->numPanes; i++)
  148.         DisposeIconSuite((*dataH)->IconHandles[i], true);
  149.     DisposePtr((Ptr) (*dataH)->IconHandles);
  150.     // Dispose of the radio group storage
  151.     for(group=(*dataH)->radio; group; group=next) {
  152.         next = group->next;
  153.         DisposePtr((Ptr) group);
  154.     }
  155.     // Dispose of everything else
  156.     DisposePtr((Ptr) (*dataH)->paneIDs);
  157.     DisposeHandle((*dataH)->tmpData);
  158.     DisposeHandle((Handle) dataH);
  159.     DisposeDialog(*dlog);
  160.     // Tell caller we closed the MPD
  161.     *dlog = NULL;
  162. }
  163.  
  164. short DoMPDialogEvent(DialogPtr *dlog, EventRecord *theEvent)
  165. {
  166.     short itemHit = 0, dblClick, part;
  167.     DialogPtr dptr;
  168.     GrafPtr oldPort;
  169.     WindowPtr window, fwin = FrontWindow();
  170.     MPDHdl dataH;
  171.     Cell theCell;
  172.     
  173.     GetPort(&oldPort);
  174.     SetPort(*dlog);
  175.  
  176.     if (!*dlog) return kNotHandled;
  177.     dataH = (MPDHdl) GetWRefCon(*dlog);
  178.  
  179.     // Handle events only for this MPD
  180.     switch (theEvent->what) {
  181.         case 0:
  182.             break;
  183.         case mouseDown:
  184.             part = FindWindow(theEvent->where, &window);
  185.             if (window != *dlog) return kNotHandled;
  186.             switch (part) {
  187.                 case inDrag:
  188.                     DragWindow(window, theEvent->where, &qd.screenBits.bounds);
  189.                     SetPort(oldPort);
  190.                     return kHandled;
  191.                 case inGoAway:
  192.                     if (TrackGoAway(window, theEvent->where)) {
  193.                         CloseMPDialog(dlog);
  194.                         return kHandled;
  195.                     }
  196.                     break;
  197.             }
  198.             if (CLClick(*dlog, theEvent, &dblClick)) itemHit = kIconBox;
  199.             break;
  200.         case keyDown:
  201.         case autoKey:
  202.             if (fwin != *dlog) return kNotHandled;
  203.             switch (theEvent->message & charCodeMask) {
  204.                 case kLeftArrow:
  205.                     theEvent->message = kUpArrow;
  206.                     goto doit;
  207.                 case kRightArrow:
  208.                     theEvent->message = kDownArrow;
  209.                     goto doit;
  210.                 case '\t':
  211.                     // Don't allow TAB to change panes if multiple edit fileds are on the pane
  212.                     if (MultipleEditFields(*dlog)) break;
  213.                     theEvent->message = (theEvent->modifiers & shiftKey) ? kUpArrow : kDownArrow;
  214.                 case kUpArrow:
  215.                 case kDownArrow:
  216.                 doit:
  217.                     if (CLKey(*dlog, theEvent)) itemHit = kIconBox;
  218.                     break;
  219.             }
  220.             break;
  221.         case updateEvt:
  222.         case activateEvt:
  223.             if ((WindowPtr) theEvent->message != *dlog) return kNotHandled;
  224.             break;
  225.         default:
  226.             return kNotHandled;
  227.     }
  228.  
  229.     // After handling non-dialog window events, try for the dialog events including
  230.     // key equivalents for both the main dialog and the current pane.
  231.     if (!itemHit && !keyAltFilter(*dlog, kKeyStrings, 0, theEvent, &itemHit) &&
  232.                 !keyAltFilter(*dlog, (*dataH)->baseItems, (*dataH)->baseItems-1, theEvent, &itemHit)) {
  233.         if (!IsDialogEvent(theEvent)) {
  234.             SetPort(oldPort);
  235.             return kNotHandled;
  236.         }
  237.         if (!DialogSelect(theEvent,&dptr,&itemHit)) {
  238.             SetPort(oldPort);
  239.             if (theEvent->what == mouseDown) return kHandled;
  240.             return kNotHandled; // Not for us
  241.         } else if (!itemHit) {
  242.             SetPort(oldPort);
  243.             return kHandled;    // For us, but not exciting
  244.         } else {                // Handle revert when EditText fields change
  245.             Handle iHandle;
  246.             short iType;
  247.             Rect iRect;
  248.             
  249.             GetDItem(*dlog, itemHit, &iType, &iHandle, &iRect);
  250.             if (iType == editText) {
  251.                 if (theEvent->what == keyDown && (theEvent->message & charCodeMask) >= ' ') {
  252.                      (*dataH)->paneDirty = true;
  253.                      AbleDItem(*dlog, kBRevert, true);
  254.                 }
  255.                 SetPort(oldPort);
  256.                 return kHandled;
  257.             }
  258.         }
  259.     }
  260.  
  261.     // Did something interesting happen?
  262.     switch (itemHit) {
  263.         case 0:
  264.             break;
  265.         case kBOk:
  266.             // Save the values of the controls and retain MPD changes
  267.             // The dialog can't be Oked if a validation error is returned
  268.             if (HandleMPDAction(*dlog, kP2TAction, (*dataH)->currentPane))
  269.                 HandleMPDAction(*dlog, kT2RAction, 0);
  270.             else break;
  271.         case kBCancel:
  272.             // Close the dialog if Oked or Canceled
  273.             CloseMPDialog(dlog);
  274.             break;
  275.         case kBRevert:
  276.             // Restore previous settings
  277.             HandleMPDAction(*dlog, kT2PAction, (*dataH)->currentPane);
  278.              (*dataH)->paneDirty = false;
  279.              AbleDItem(*dlog, kBRevert, false);
  280.             break;
  281.         case kBFactory:
  282.             // Restore factory defaults
  283.             SetFactoryDefault(*dlog, (*dataH)->currentPane);
  284.              (*dataH)->paneDirty = true;
  285.              AbleDItem(*dlog, kBRevert, true);
  286.             break;
  287.         case kIconBox:
  288.             // Has the pane changed?
  289.             if (!(*dataH)->theList) return kHandled;
  290.             theCell.h = theCell.v = 0;
  291.             if (LGetSelect(true, &theCell, (*dataH)->theList)) {
  292.                 theCell.v++;
  293.                 if (theCell.v != (*dataH)->currentPane) {
  294.                     if (!HandleMPDAction(*dlog, kP2TAction, (*dataH)->currentPane)) {
  295.                         // We can't change the pane now
  296.                         theCell.v--;
  297.                         LSetSelect(false, theCell, (*dataH)->theList);
  298.                         theCell.v = (*dataH)->currentPane - 1;
  299.                         LSetSelect(true, theCell, (*dataH)->theList);
  300.                         return kHandled;
  301.                     }
  302.                     // Change the pane
  303.                     (*dataH)->currentPane = theCell.v;
  304.                     InstallDITL(*dlog, theCell.v);
  305.                     HandleMPDAction(*dlog, kT2PAction, theCell.v);
  306.                  }
  307.              }
  308.              break;
  309.          default:
  310.              // A control was clicked
  311.              CallClickActionProc((*dataH)->ClickAction, kClickAction, *dlog, (*dataH)->currentPane, itemHit);
  312.              if (!(*dataH)->paneDirty) {
  313.                  (*dataH)->paneDirty = true;
  314.                  AbleDItem(*dlog, kBRevert, true);
  315.              }
  316.              break;
  317.     }
  318.  
  319.     SetPort(oldPort);
  320.     return kHandled;
  321. }
  322.  
  323. Boolean MultipleEditFields(DialogPtr dlog)
  324. {
  325.     MPDHdl dataH;
  326.     short numItems, i, iType, numEdits = 0;
  327.     Handle iHandle;
  328.     Rect iRect;
  329.  
  330.     if (!dlog) return false;
  331.     dataH = (MPDHdl) GetWRefCon(dlog);
  332.     numItems = CountDITL(dlog);
  333.     
  334.     for(i=(*dataH)->baseItems; i<=numItems; i++) {
  335.         GetDItem(dlog, i, &iType, &iHandle, &iRect);
  336.         if (iType == editText) numEdits++;
  337.     }
  338.     return ((numEdits > 1) ? true : false);
  339. }
  340.  
  341. // UniversalProcPtr stuff for default Action Procedures
  342. #if USESROUTINEDESCRIPTORS
  343. RoutineDescriptor gClickActionRoutine = BUILD_ROUTINE_DESCRIPTOR(kClickProcInfo, DefaultClickAction);
  344. RoutineDescriptor gEditActionRoutine = BUILD_ROUTINE_DESCRIPTOR(kEditProcInfo, DefaultEditAction);
  345. RoutineDescriptor gGroupActionRoutine = BUILD_ROUTINE_DESCRIPTOR(kGroupProcInfo, DefaultGroupAction);
  346. RoutineDescriptor gDefActionRoutine = BUILD_ROUTINE_DESCRIPTOR(kDefProcInfo, DefaultAction);
  347. ClickActionUPP gClickActionProc = &gClickActionRoutine;
  348. EditActionUPP gEditActionProc = &gEditActionRoutine;
  349. GroupActionUPP gGroupActionProc = &gGroupActionRoutine;
  350. DefActionUPP gDefActionProc = &gDefActionRoutine;
  351. #else
  352. ClickActionUPP gClickActionProc = (ClickActionUPP) DefaultClickAction;
  353. EditActionUPP gEditActionProc = (EditActionUPP) DefaultEditAction;
  354. GroupActionUPP gGroupActionProc = (GroupActionUPP) DefaultGroupAction;
  355. DefActionUPP gDefActionProc = (DefActionUPP) DefaultAction;
  356. #endif
  357.  
  358. short InstallAction(short aType, DialogPtr dlog, UniversalProcPtr actionProc)
  359. {
  360.     MPDHdl dataH;
  361.     
  362.     dataH = (MPDHdl) GetWRefCon(dlog);
  363.     switch (aType) {
  364.         case kItemAction:
  365.             (*dataH)->ClickAction = actionProc ? (ClickActionUPP) actionProc : gClickActionProc;
  366.             break;
  367.         case kEditAction:
  368.             (*dataH)->EditAction = actionProc ? (EditActionUPP) actionProc : gEditActionProc;
  369.             break;
  370.         case kGroupAction:
  371.             (*dataH)->GroupAction = actionProc ? (GroupActionUPP) actionProc : gGroupActionProc;
  372.             break;
  373.         case kDefAction:
  374.             (*dataH)->DefAction = actionProc ? (DefActionUPP) actionProc : gDefActionProc;
  375.             break;
  376.         default:
  377.             return kNotHandled;
  378.     }
  379.     return kHandled;
  380. }
  381.  
  382. short RemoveAction(short aType, DialogPtr dlog)
  383. {
  384.     return InstallAction(aType, dlog, NULL);
  385. }
  386.  
  387. /********************************************************************************
  388.  *
  389.  * Multi-Pane Dialog Data Access Routines
  390.  *
  391.  ********************************************************************************/
  392.  
  393. short GetMPDItem(Handle theData, short pane, short item, Ptr ptr, short len)
  394. {
  395.     Ptr hPtr;
  396.     short i;
  397.  
  398.     if (!theData) return keNullData;
  399.     hPtr = *theData + sizeof(long);
  400.     
  401.     // Find the correct pane…
  402.     for(i=1; i<pane; i++) {
  403.         hPtr += 4;
  404.         if (*(long *) hPtr == 0L) return keBadPane;
  405.     }
  406.     hPtr = *theData + sizeof(long) + *(long *) hPtr;
  407.     
  408.     // Find the correct item…
  409.     for(item--;item; item--) {
  410.         hPtr += (*(short *) hPtr) + sizeof(short);
  411.         if (!*(short *) hPtr) return keBadItem;
  412.     }
  413.     
  414.     // Get the value
  415.     if (len != *(short *) hPtr) return keWrongSize;
  416.     BlockMove(hPtr+sizeof(short), ptr, *(short *) hPtr);
  417.     return noErr;
  418. }
  419.  
  420. short SetMPDItem(Handle theData, short pane, short item, Ptr ptr, short len)
  421. {
  422.     Ptr hPtr;
  423.     short i;
  424.  
  425.     if (!theData) return keNullData;
  426.     HLock(theData);
  427.     hPtr = *theData + sizeof(long);
  428.     
  429.     // Find the correct pane…
  430.     for(i=1; i<pane; i++) {
  431.         hPtr += 4;
  432.         if (*(long *) hPtr == 0L) return keBadPane;
  433.     }
  434.     hPtr = *theData + sizeof(long) + *(long *) hPtr;
  435.     
  436.     // Find the correct item…
  437.     for(item--;item; item--) {
  438.         hPtr += (*(short *) hPtr) + sizeof(short);
  439.         if (!*(short *) hPtr) return keBadItem;
  440.     }
  441.  
  442.     // Set the value
  443.     if (len != *(short *) hPtr) return keWrongSize;
  444.     BlockMove(ptr, hPtr+sizeof(short), *(short *) hPtr);
  445.     return noErr;
  446. }
  447.  
  448. /********************************************************************************
  449.  *
  450.  * Internal Multi-Pane Dialog Routines
  451.  *
  452.  ********************************************************************************/
  453.  
  454. void InstallDITL(DialogPtr dlog, short pane)
  455. {
  456.     MPDHdl dataH;
  457.     Handle theDITL, iHandle;
  458.     short i, num, iType;
  459.     Rect iRect;
  460.     
  461.     dataH = (MPDHdl) GetWRefCon(dlog);
  462.     // Pane is now no longer dirty
  463.      (*dataH)->paneDirty = false;
  464.      AbleDItem(dlog, kBRevert, false);
  465.     
  466.     // Remove old pane
  467.     i = CountDITL(dlog) - (*dataH)->baseItems + 1;
  468.     ShortenDITL(dlog, i);
  469.     
  470.     // Install new one
  471.     theDITL = GetResource('DITL', (*dataH)->paneIDs[pane-1]);
  472.     if (!theDITL) return;
  473.     AppendDITL(dlog, theDITL, overlayDITL);
  474.     ReleaseResource(theDITL);
  475.     
  476.     // Set all user items to be the gray box
  477.     num = CountDITL(dlog);
  478.     for(i=(*dataH)->baseItems; i<=num; i++) {
  479.         GetDItem(dlog, i, &iType, &iHandle, &iRect);
  480.         if ((iType & 0x7f) == userItem)
  481.             SetItemToGray(dlog, i);
  482.     }
  483. }
  484.  
  485. Handle MPDTemplateFromDITL(MPDHdl dataH)
  486. {
  487.     short ditl, i, dcount, iType, pseudoItem;
  488.     short num = (*dataH)->numPanes, *DITList = (*dataH)->paneIDs;
  489.     Handle h, iHandle, theData;
  490.     DialogPtr dptr;
  491.     Rect wRect, iRect;
  492.     long hSize, tSize;
  493.     DialogRecord storage;
  494.     
  495.     hSize = sizeof(long) * (num + 1);
  496.     theData = NewHandle(hSize);
  497.     MoveHHi(theData);
  498.     SetRect(&wRect, 0, 0, 50, 50);
  499.     
  500.     // Process all the panes…
  501.     for(ditl=0; ditl<num; ditl++) {
  502.         // Load the pane's DITL so we can run through it, without knowing it's structure.
  503.         ((long *) *theData)[ditl] = hSize;
  504.         h = GetResource('DITL', DITList[ditl]);
  505.         DetachResource(h);
  506.         if (!h) continue; // Oops!
  507.         dptr = NewDialog(&storage, &wRect, "\p", false, dBoxProc, (WindowPtr) -1, false, 0, h);
  508.         if (!dptr) continue; // Oops!
  509.         
  510.         // Process each item in the pane…
  511.         dcount = CountDITL(dptr);
  512.         pseudoItem = 1; // start with item 1
  513.         for(i=1; i<=dcount; i++) {
  514.             GetDItem(dptr, i, &iType, &iHandle, &iRect);
  515.             switch(iType & 0x7f) {
  516.                 case 4: // Button
  517.                 case 5: // Check box
  518.                 case 7: // Control
  519.                     SetHandleSize(theData, hSize+4);
  520.                     if (MemError()) goto error;
  521.                     *(short *) ((*theData) + hSize) = sizeof(short);
  522.                     hSize += sizeof(short);
  523.                     HLock(theData);
  524.                     CallDefActionProc((*dataH)->DefAction ,(*theData) + hSize, sizeof(short),
  525.                         iType, ditl+1, pseudoItem);
  526.                     HUnlock(theData);
  527.                     hSize += sizeof(short);
  528.                     pseudoItem++;
  529.                     break;
  530.                 case 6: // Radio Button
  531.                     tSize = GetRadioMPDSize((*dataH)->radio, ditl+1, i);
  532.                     // If the Radio Button is not part of a radio group,
  533.                     // handle it as a check box
  534.                     if (tSize) {
  535.                         SetHandleSize(theData, hSize+sizeof(short)+tSize);
  536.                         if (MemError()) goto error;
  537.                         *(short *) ((*theData) + hSize) = tSize;
  538.                         hSize += sizeof(short);
  539.                         HLock(theData);
  540.                         CallDefActionProc((*dataH)->DefAction, (*theData) + hSize, sizeof(short),
  541.                             iType, ditl+1, pseudoItem);
  542.                         HUnlock(theData);
  543.                         hSize += tSize;
  544.                         pseudoItem++;
  545.                     }
  546.                     break;
  547.                 case editText: // TextEdit field
  548.                     tSize = CallEditActionProc((*dataH)->EditAction,
  549.                             kCalcAction, NULL, iHandle, ditl+1, pseudoItem);
  550.                     SetHandleSize(theData, hSize+sizeof(short)+tSize);
  551.                     if (MemError()) goto error;
  552.                     *(short *) ((*theData) + hSize) = tSize;
  553.                     hSize += sizeof(short);
  554.                     HLock(theData);
  555.                     CallEditActionProc((*dataH)->EditAction,
  556.                             kInitAction, (*theData) + hSize, iHandle, ditl+1, pseudoItem);
  557.                     HUnlock(theData);
  558.                     hSize += tSize;
  559.                     pseudoItem++;
  560.                     break;
  561.                 default:
  562.                     break;
  563.             }
  564.         }
  565.  
  566.         // When done with all the normal items, do the radio grouped items
  567.         HLock(theData);
  568.         tSize = DoGroupAction(kCalcAction, (WindowPtr) dataH, NULL, ditl+1, NULL);
  569.         HUnlock(theData);
  570.         if (tSize) {
  571.             SetHandleSize(theData, hSize+tSize);
  572.             if (MemError()) goto error;
  573.             DoGroupAction(kInitAction, (WindowPtr) dataH, *theData+hSize, ditl+1, &pseudoItem);
  574.             hSize += tSize;
  575.         }
  576.         SetHandleSize(theData, hSize+sizeof(short));
  577.         if (MemError()) goto error;
  578.         *(short *) &(*theData)[hSize] = 0;
  579.         hSize += sizeof(short);
  580.         CloseDialog(dptr);
  581.     }
  582.     ((long *) *theData)[ditl] = 0L;
  583.     if (hSize != GetHandleSize(theData)) SysBeep(3);
  584.     SetHandleSize(theData, hSize + sizeof(long));
  585.     if (MemError()) goto error;
  586.     BlockMove(*theData, *theData + sizeof(long), hSize);
  587.     *(long *) *theData = 1;
  588.     return MemError() ? NULL : theData;
  589.     
  590.     error:
  591.         DisposeHandle(theData);
  592.         if (dptr) CloseDialog(dptr);
  593.         return NULL;
  594. }
  595.  
  596. short CreateMPDRadioGroup(DialogPtr dlog, short pane, short *itemList, short itemNum)
  597. {
  598.     MPDHdl dataH;
  599.     RadioGroupPtr group;
  600.     
  601.     dataH = (MPDHdl) GetWRefCon(dlog);
  602.     group = (RadioGroupPtr) NewPtr(sizeof(RadioGroup) + itemNum * sizeof(short));
  603.     if (!group) return dsMemFullErr;
  604.     BlockMove(itemList, &group->items, itemNum * sizeof(short));
  605.     group->num = itemNum;
  606.     group->pane = pane;
  607.     group->next = (*dataH)->radio;
  608.     (*dataH)->radio = group;
  609.     return noErr;
  610. }
  611.  
  612. void CreateRadioGroups(DialogPtr dlog, short pane)
  613. {
  614.     MPDHdl dataH;
  615.     Handle h;
  616.     short g, numGroups, numItems;
  617.     short *p = NULL;
  618.  
  619.     dataH = (MPDHdl) GetWRefCon(dlog);
  620.     
  621.     if (!(h = GetResource(kGROUP, (*dataH)->paneIDs[pane]))) return;
  622.     MoveHHi(h);
  623.     HLock(h);
  624.     p = (short *) *h;
  625.     numGroups = *p++;
  626.     
  627.     for(g=0; g<numGroups; g++) {
  628.         numItems = *p++;
  629.         CreateMPDRadioGroup(dlog, pane+1, p, numItems);
  630.         p += numItems;
  631.     }
  632.     
  633.     HUnlock(h);
  634.     ReleaseResource(h);
  635. }
  636.  
  637. void SetFactoryDefault(DialogPtr dlog, short pane)
  638. {
  639.     short i, iType, tSize, hilited = false, val, pseudoItem = 1;
  640.     Handle iHandle;
  641.     Rect iRect;
  642.     MPDHdl dataH;
  643.     RadioGroupPtr radio;
  644.     Ptr tmpPtr;
  645.     
  646.     dataH = (MPDHdl) GetWRefCon(dlog);
  647.     HLock((*dataH)->theData);
  648.     radio = (*dataH)->radio;
  649.  
  650.     for(i=(*dataH)->baseItems; i<=CountDITL(dlog); i++) {
  651.         GetDItem(dlog, i, &iType, &iHandle, &iRect);
  652.         switch(iType & 0x7f) {
  653.             case 4: // Button
  654.             case 5: // Check box
  655.             case 7: // Control
  656.                 CallDefActionProc((*dataH)->DefAction, (Ptr) &val, sizeof(short), iType,
  657.                     pane, pseudoItem);
  658.                 SetCtlValue((ControlHandle) iHandle, val);
  659.                 pseudoItem++;
  660.                 break;
  661.             case 6: // Radio Button
  662.                 if (tSize = GetRadioMPDSize(radio, pane, i - (*dataH)->baseItems + 1)) {
  663.                     CallDefActionProc((*dataH)->DefAction, (Ptr) &val, sizeof(short),
  664.                         iType, pane, pseudoItem);
  665.                     SetCtlValue((ControlHandle) iHandle, val);
  666.                     pseudoItem++;
  667.                 }
  668.                 break;
  669.             case editText:
  670.                 // Allocate a temporart pointer to hold the factory value, get the
  671.                 // default value, and finally set the control.
  672.                 tSize = CallEditActionProc((*dataH)->EditAction, 
  673.                         kCalcAction, NULL, iHandle, pane, pseudoItem);
  674.                 if (!(tmpPtr = NewPtr(tSize))) break;
  675.                 CallEditActionProc((*dataH)->EditAction,
  676.                         kInitAction, tmpPtr, iHandle, pane, pseudoItem);
  677.                 CallEditActionProc((*dataH)->EditAction,
  678.                         kT2PAction, tmpPtr, iHandle, pane, pseudoItem);
  679.                 DisposePtr(tmpPtr);
  680.                 // Hilite first EditText field in the dialog
  681.                 if (!hilited) {
  682.                     SelIText(dlog, i, 0, 32768);
  683.                     hilited = true;
  684.                 }
  685.                 pseudoItem++;
  686.                 break;
  687.             default:
  688.                 break;
  689.         }
  690.     }
  691.     
  692.     // After handling the normal controls, handle the radio grouped controls
  693.     tSize = DoGroupAction(kCalcAction, (WindowPtr) dataH, NULL, pane, NULL);
  694.     if (tSize) {
  695.         if (!(tmpPtr = NewPtr(tSize))) return;
  696.         DoGroupAction(kInitAction, (WindowPtr) dataH, tmpPtr, pane, &val);
  697.         DoGroupAction(kT2PAction, dlog, tmpPtr, pane, &pseudoItem);
  698.         DisposePtr(tmpPtr);
  699.     }
  700.     
  701.     HUnlock((*dataH)->theData);
  702.     return;
  703. }
  704.  
  705. void ToggleGroup(DialogPtr dlog, RadioGroupPtr group, short item)
  706. {
  707.     MPDHdl dataH;
  708.     short i;
  709.  
  710.     dataH = (MPDHdl) GetWRefCon(dlog);
  711.     for(i=0; i<group->num; i++) {
  712.         SetCheckOrRadioButton(dlog, group->items[i] + (*dataH)->baseItems - 1, group->items[i] == item);
  713.     }
  714. }
  715.  
  716. short GetRadioMPDSize(RadioGroupPtr radio, short pane, short item)
  717. {
  718.     RadioGroupPtr group;
  719.     short i, ret = sizeof(short);
  720.     
  721.     // If the control is a member of a radio group, return zero, else return sizeof(short).
  722.     for(group=radio; group; group=group->next) {
  723.         if (group->pane == pane)
  724.             for(i=0; i<group->num; i++) {
  725.                 if (group->items[i] == item) ret = 0;
  726.             }
  727.     }
  728.     return ret;
  729. }
  730.  
  731. /********************************************************************************
  732.  *
  733.  * Internal Multi-Pane Dialog Action Procedures
  734.  *
  735.  ********************************************************************************/
  736.  
  737. short HandleMPDAction(DialogPtr dlog, short message, short pane)
  738. {
  739.     MPDHdl dataH;
  740.     long hSize;
  741.     
  742.     dataH = (MPDHdl) GetWRefCon(dlog);
  743.     hSize = GetHandleSize((*dataH)->theData);
  744.     switch (message) {
  745.         case kR2TAction:    // Transfer real to temporary
  746.             HLock((*dataH)->theData);
  747.             HLock((*dataH)->tmpData);
  748.             BlockMove(*(*dataH)->theData, *(*dataH)->tmpData, hSize);
  749.             HUnlock((*dataH)->theData);
  750.             HUnlock((*dataH)->tmpData);
  751.             break;
  752.         case kP2TAction:    // Transfer controls to temporary
  753.             return P2TMPDAction(dlog, pane);
  754.         case kT2PAction:    // Transfer temporary to controls
  755.             return T2PMPDAction(dlog, pane);
  756.         case kT2RAction:    // Transfer temporary to real
  757.             HLock((*dataH)->theData);
  758.             HLock((*dataH)->tmpData);
  759.             BlockMove(*(*dataH)->tmpData, *(*dataH)->theData, hSize);
  760.             HUnlock((*dataH)->theData);
  761.             HUnlock((*dataH)->tmpData);
  762.             break;
  763.         default:
  764.             return kNotHandled;
  765.     }
  766.     return kHandled;
  767. }
  768.  
  769. short P2TMPDAction(DialogPtr dlog, short pane)
  770. {
  771.     short i, iType, tSize, pseudoItem = 1, sSize;
  772.     Handle iHandle;
  773.     Rect iRect;
  774.     MPDHdl dataH;
  775.     Ptr hPtr;
  776.     RadioGroupPtr radio;
  777.     
  778.     dataH = (MPDHdl) GetWRefCon(dlog);
  779.     HLock((*dataH)->theData);
  780.     hPtr = *(*dataH)->tmpData + sizeof(long) + ((long *) *(*dataH)->tmpData)[pane];
  781.     radio = (*dataH)->radio;
  782.  
  783.     // Validate the pane, returning false if it doesn't
  784.     for(i=(*dataH)->baseItems; i<=CountDITL(dlog); i++) {
  785.         GetDItem(dlog, i, &iType, &iHandle, &iRect);
  786.         // Abort transfer if something's gone awry…
  787.          if (CallClickActionProc((*dataH)->ClickAction, kValidateAction, dlog, pane, i))
  788.              return false;
  789.     }
  790.     
  791.     // Store the control values into memory
  792.     for(i=(*dataH)->baseItems; i<=CountDITL(dlog); i++) {
  793.         GetDItem(dlog, i, &iType, &iHandle, &iRect);
  794.          
  795.         switch(iType & 0x7f) {
  796.             case 4: // Button
  797.             case 5: // Check box
  798.             case 7: // Control
  799.                 sSize = *(short *) hPtr;
  800.                 hPtr += sizeof(short);
  801.                 *(short *) hPtr = GetCtlValue((ControlHandle) iHandle);
  802.                 hPtr += sSize;
  803.                 pseudoItem++;
  804.                 break;
  805.             case 6: // Radio Button
  806.                 if (tSize = GetRadioMPDSize(radio, pane, i - (*dataH)->baseItems + 1)) {
  807.                     sSize = *(short *) hPtr;
  808.                     hPtr += sizeof(short);
  809.                     *(short *) hPtr = GetCtlValue((ControlHandle) iHandle);
  810.                     hPtr += sSize;
  811.                     pseudoItem++;
  812.                 }
  813.                 break;
  814.             case editText:
  815.                 sSize = *(short *) hPtr;
  816.                 hPtr += sizeof(short);
  817.                 hPtr += CallEditActionProc((*dataH)->EditAction,
  818.                         kP2TAction, hPtr, iHandle, pane, pseudoItem);
  819.                 hPtr += sSize;
  820.                 pseudoItem++;
  821.                 break;
  822.             default:
  823.                 break;
  824.         }
  825.     }
  826.     hPtr += DoGroupAction(kP2TAction, dlog, hPtr, pane, &pseudoItem);
  827.     HUnlock((*dataH)->theData);
  828.     return true;
  829. }
  830.  
  831. short T2PMPDAction(DialogPtr dlog, short pane)
  832. {
  833.     short i, iType, tSize, hilited = false, pseudoItem = 1, sSize;
  834.     Handle iHandle;
  835.     Rect iRect;
  836.     MPDHdl dataH;
  837.     Ptr hPtr;
  838.     RadioGroupPtr radio;
  839.     
  840.     dataH = (MPDHdl) GetWRefCon(dlog);
  841.     HLock((*dataH)->theData);
  842.     hPtr = *(*dataH)->tmpData + sizeof(long) + ((long *) *(*dataH)->tmpData)[pane];
  843.     radio = (*dataH)->radio;
  844.  
  845.     // Set the controls based on the values in memory
  846.     for(i=(*dataH)->baseItems; i<=CountDITL(dlog); i++) {
  847.         GetDItem(dlog, i, &iType, &iHandle, &iRect);
  848.         switch(iType & 0x7f) {
  849.             case 4: // Button
  850.             case 5: // Check box
  851.             case 7: // Control
  852.                 sSize = *(short *) hPtr;
  853.                 hPtr += sizeof(short);
  854.                 SetCtlValue((ControlHandle) iHandle, *(short *) hPtr);
  855.                 hPtr += sSize;
  856.                 pseudoItem++;
  857.                 break;
  858.             case 6: // Radio Button
  859.                 if (tSize = GetRadioMPDSize(radio, pane, i - (*dataH)->baseItems + 1)) {
  860.                     sSize = *(short *) hPtr;
  861.                     hPtr += sizeof(short);
  862.                     SetCtlValue((ControlHandle) iHandle, *(short *) hPtr);
  863.                     hPtr += sSize;
  864.                     pseudoItem++;
  865.                 }
  866.                 break;
  867.             case editText:
  868.                 sSize = *(short *) hPtr;
  869.                 hPtr += sizeof(short);
  870.                 hPtr += CallEditActionProc((*dataH)->EditAction, 
  871.                         kT2PAction, hPtr, iHandle, pane, pseudoItem);
  872.                 if (!hilited) {
  873.                     SelIText(dlog, i, 0, 32768);
  874.                     hilited = true;
  875.                 }
  876.                 hPtr += sSize;
  877.                 pseudoItem++;
  878.                 break;
  879.             default:
  880.                 break;
  881.         }
  882.         CallClickActionProc((*dataH)->ClickAction, kInitAction, dlog, pane, i);
  883.     }
  884.     hPtr += DoGroupAction(kT2PAction, dlog, hPtr, pane, &pseudoItem);
  885.     HUnlock((*dataH)->theData);
  886.     return true;
  887. }
  888.  
  889. short HandleRadioGroup(DialogPtr dlog, short item)
  890. {
  891.     MPDHdl dataH;
  892.     RadioGroupPtr group;
  893.     short i, pane;
  894.     short ret = kNotHandled;
  895.  
  896.     dataH = (MPDHdl) GetWRefCon(dlog);
  897.     pane = (*dataH)->currentPane;
  898.     
  899.     // Return kHandled if the control was in a radio group,
  900.     // otherwise return kNotHandled
  901.     for(group=(*dataH)->radio; group; group=group->next) {
  902.         if (group->pane != pane) continue;
  903.         for(i=0; i<group->num; i++) {
  904.             // We allow a radio control to be a member of more than one group.
  905.             if (group->items[i] == item) {
  906.                 ret = kHandled;
  907.                 ToggleGroup(dlog, group, item);
  908.             }
  909.         }
  910.     }
  911.     return ret;
  912. }
  913.  
  914. short DoGroupAction(short mType, DialogPtr dlog, Ptr hPtr, short pane, short *item)
  915. {
  916.     short ret = 0, val;
  917.     MPDHdl dataH;
  918.     RadioGroupPtr group;
  919.  
  920.     switch (mType) {
  921.         case kP2TAction:
  922.             dataH = (MPDHdl) GetWRefCon(dlog);
  923.             break;
  924.         case kT2PAction:
  925.             dataH = (MPDHdl) GetWRefCon(dlog);
  926.             break;
  927.         case kCalcAction:
  928.         case kInitAction:
  929.             dataH = (MPDHdl) dlog;
  930.             dlog = NULL;
  931.             break;
  932.         default:
  933.             return kNotHandled;
  934.     }
  935.     
  936.     for(group=(*dataH)->radio; group; group=group->next) {
  937.         if (group->pane == pane) {
  938.             val = CallGroupActionProc((*dataH)->GroupAction, 
  939.                     mType, group, (Handle) dataH, dlog, hPtr, pane, *item);
  940.             if (item) {
  941.                 (*item)++;
  942.                 hPtr += val;
  943.             }
  944.             ret += val;
  945.         }
  946.     }
  947.     return ret;
  948. }
  949.  
  950. /********************************************************************************
  951.  *
  952.  * Default Action Procedures
  953.  *
  954.  ********************************************************************************/
  955.  
  956. void DefaultAction(Ptr theData, short len, short iType, short pane, short item)
  957. {
  958.     switch (iType & 0x7f) {
  959.         case 4: // Button
  960.         case 5: // Check Box
  961.         case 6: // Radio Button
  962.         case 7: // Control
  963.             if (len == sizeof(short)) *(short *) theData = 0;
  964.             break;
  965.     }
  966. }
  967.  
  968. short DefaultEditAction(short mType, Ptr hPtr, Handle iHandle, short pane, short item)
  969. {
  970.     short ret = 0;
  971.     Str255 textStr;
  972.     
  973.     switch (mType) {
  974.         case kP2TAction:
  975.             GetIText(iHandle, textStr);
  976.             Pstrcpy((unsigned char *) hPtr, textStr);
  977.             ret = 256;
  978.             break;
  979.         case kT2PAction:
  980.             Pstrcpy(textStr, (unsigned char *) hPtr);
  981.             SetIText(iHandle, textStr);
  982.             ret = 256;
  983.             break;
  984.         case kCalcAction:
  985.             ret = 256;
  986.             break;
  987.         case kInitAction:
  988.             *hPtr = 0;
  989.             break;
  990.         default:
  991.             break;
  992.     }
  993.     return ret;
  994. }
  995.  
  996. short DefaultClickAction(short mType, DialogPtr dlog, short pane, short item)
  997. {
  998.     MPDHdl dataH;
  999.     short iType;
  1000.     Rect iRect;
  1001.     Handle iHandle;
  1002.  
  1003.     switch (mType) {
  1004.         case kValidateAction:    // No default validation
  1005.         case kInitAction:        // No default initialization actions
  1006.             return noErr;
  1007.     }
  1008.     // mType == kClickAction
  1009.  
  1010.     dataH = (MPDHdl) GetWRefCon(dlog);
  1011.     GetDItem(dlog, item, &iType, &iHandle, &iRect);
  1012.     switch(iType & 0x7f) {
  1013.         case 4: // Button
  1014.             // No default action for buttons
  1015.             break;
  1016.         case 5: // Check
  1017.              ToggleCheck(dlog, item);
  1018.             break;
  1019.         case 6: // Radio
  1020.             if (!HandleRadioGroup(dlog, item - (*dataH)->baseItems + 1))
  1021.                 ToggleCheck(dlog, item);
  1022.             break;
  1023.         case 7: // CTRL
  1024.             // They handle themselves.
  1025.             break;
  1026.     }
  1027.     return noErr;
  1028. }
  1029.  
  1030. short DefaultGroupAction(short mType, RadioGroupPtr group, Handle H, DialogPtr dlog,
  1031.     Ptr hPtr, short pane, short item)
  1032. {
  1033.     short i, value, ret = 0;
  1034.     MPDHdl dataH = (MPDHdl) H;
  1035.     
  1036.     switch (mType) {
  1037.         case kP2TAction:
  1038.             hPtr += sizeof(short);
  1039.             for(i=0; i<group->num; i++) {
  1040.                 if (GetCheckOrRadio(dlog, group->items[i] + (*dataH)->baseItems - 1))
  1041.                     *(short *) hPtr = i + 1;
  1042.             }
  1043.             ret += sizeof(short) + sizeof(short);
  1044.             break;
  1045.         case kT2PAction:
  1046.             hPtr += sizeof(short);
  1047.             for(i=0; i<group->num; i++) {
  1048.                 if (value = (*(short *) hPtr) == i + 1)
  1049.                     SetCheckOrRadioButton(dlog, group->items[i] + (*dataH)->baseItems - 1, value);
  1050.                 else SetCheckOrRadioButton(dlog, group->items[i] + (*dataH)->baseItems - 1, 0);
  1051.             }
  1052.             ret += sizeof(short) + sizeof(short);
  1053.             break;
  1054.         case kCalcAction:
  1055.             ret += sizeof(short) + sizeof(short);
  1056.             break;
  1057.         case kInitAction:
  1058.             *(short *) hPtr = sizeof(short);
  1059.             hPtr += sizeof(short);
  1060.             *(short *) hPtr = 1;
  1061.             hPtr += sizeof(short);
  1062.             ret += sizeof(short) + sizeof(short);
  1063.             break;
  1064.     }
  1065.     
  1066.     return ret;
  1067. }
  1068.  
  1069.  
  1070. /********************************************************************************
  1071.  *
  1072.  * Utility Functions
  1073.  *
  1074.  ********************************************************************************/
  1075.  
  1076.  
  1077. /* Custom dialog item which simply draws a gray box.
  1078.  * If run on a Mac without Color QuickDraw or in B&W mode, it draws a
  1079.  * stippled gray line, otherwise it draws a real gray line.
  1080.  */
  1081. pascal void DrawGray (DialogPtr theDialog, short theItem)
  1082. {
  1083.     Rect iRect;
  1084.     short iKind;
  1085.     Handle iHandle;
  1086.     RGBColor oBack, oFore, gFore;
  1087.     GrafPtr oldPort;
  1088.  
  1089.     GetPort(&oldPort);
  1090.     SetPort(theDialog);
  1091.     GetDItem (theDialog, theItem, &iKind, &iHandle, &iRect);
  1092.     if (gQDVersion && (*((CGrafPtr) qd.thePort)->portPixMap)->pixelSize > 1) {
  1093.         GetForeColor(&oFore);
  1094.         GetBackColor(&oBack);
  1095.         gFore = oFore;
  1096.         if (gQDVersion == 2 && GetGray(GetGDevice(), &oBack, &gFore)) {
  1097.             RGBForeColor(&gFore);
  1098.         } else {
  1099.             gFore.red = (oFore.red + oBack.red) / 2;
  1100.             gFore.green = (oFore.green + oBack.green) / 2;
  1101.             gFore.blue = (oFore.blue + oBack.blue) / 2;
  1102.             RGBForeColor(&gFore);
  1103.         }
  1104.     } else {
  1105.         PenPat(&qd.gray);
  1106.     }
  1107.     
  1108.     // Actuall draw the line. We really draw a box, so one could use this code
  1109.     // for doing other things.
  1110.     FrameRect(&iRect);
  1111.     
  1112.     // Put things back the way it was.
  1113.     if (gQDVersion && (*((CGrafPtr) qd.thePort)->portPixMap)->pixelSize > 1) {
  1114.         RGBForeColor(&oFore);
  1115.         RGBBackColor(&oBack);
  1116.     } else {
  1117.         PenPat(&qd.black);
  1118.     }
  1119.     SetPort(oldPort);
  1120. }
  1121.  
  1122. #if USESROUTINEDESCRIPTORS
  1123. RoutineDescriptor gGrayRoutine = BUILD_ROUTINE_DESCRIPTOR(uppUserItemProcInfo, DrawGray);
  1124. UniversalProcPtr gGrayProc = &gGrayRoutine;
  1125. #else
  1126. UniversalProcPtr gGrayProc = (UniversalProcPtr) DrawGray;
  1127. #endif
  1128.  
  1129. /* Sets a dialog user item to be a gray box.
  1130.  * Uses UPPs for native PowerPC support.
  1131.  */
  1132. void SetItemToGray(DialogPtr dlog, short item)
  1133. {
  1134.     Handle iHandle;
  1135.     short iKind;
  1136.     Rect iRect;
  1137.     
  1138.     GetDItem (dlog, item, &iKind, &iHandle, &iRect);
  1139.     SetDItem(dlog, item, iKind, (Handle) gGrayProc, &iRect);
  1140. }
  1141.  
  1142. /* Hilite a dialog item.
  1143.  */
  1144. void    HiliteItem(DialogPtr dlgPtr, short itemNo, short state)
  1145. {
  1146.     short    iKind;
  1147.     Handle    iHandle;
  1148.     Rect    iRect;
  1149.  
  1150.     GetDItem(dlgPtr, itemNo, &iKind, &iHandle, &iRect);
  1151.     HiliteControl((ControlHandle) iHandle, state);
  1152. }
  1153.  
  1154. /* Enable or disable a menu item.
  1155.  */
  1156. void AbleDisItem(short menu, short item, short flag)
  1157. {
  1158.     MenuHandle theMenu = GetMHandle(menu);
  1159.     
  1160.     if (!theMenu) return;
  1161.     
  1162.     if (flag)
  1163.         EnableItem(theMenu, item);
  1164.     else
  1165.         DisableItem(theMenu, item);
  1166. }
  1167.  
  1168. /* Draw a Grow Icon in a window without those silly lines.
  1169.  */
  1170. void MyDrawGrow(WindowPtr win)
  1171. {
  1172.     RgnHandle curClip, rgn;
  1173.     Rect rct;
  1174.     
  1175.     if (rgn = NewRgn()) {
  1176.         rct = win->portRect;
  1177.         rct.left = rct.right - 15;
  1178.         rct.top = rct.bottom - 15;
  1179.         curClip = NewRgn();
  1180.         GetClip(curClip);
  1181.         RectRgn(rgn,&rct);
  1182.         SetClip(rgn);
  1183.         DrawGrowIcon(win);
  1184.         SetClip(curClip);
  1185.         DisposeRgn(curClip);
  1186.         DisposeRgn(rgn);
  1187.     }
  1188. }
  1189.  
  1190. /* Get the item handle for a dialog item.
  1191.  */
  1192. ControlHandle GetItemHandle(DialogPtr theDialog, short item)
  1193. {
  1194.     ControlHandle iHandle;
  1195.     Rect iRect;
  1196.     short iType;
  1197.     
  1198.     GetDItem(theDialog,item,&iType,(Handle *) &iHandle,&iRect);
  1199.     return iHandle;
  1200. }
  1201.  
  1202. /* Specialized version of KeyEquivFilter (in Uilities.c) to read the
  1203.  *   key equivalent string from a different item number (theItem) and
  1204.  *   to add an offset to the activated item, to allow DITL-local
  1205.  *   item numbering in the key equivalent string.
  1206.  */
  1207. pascal Boolean    keyAltFilter(DialogPtr dlg, short theItem, short offset, EventRecord *event, short *item)
  1208. {
  1209.     short    itemType;
  1210.     Handle    itemHndl;
  1211.     Rect    itemRect;
  1212.     Str255    itemText;
  1213.     short    i, theChr, theMod, equivChr, modMask, modVal, itemNum;
  1214.     long    tick;
  1215.  
  1216.     if (event->what == updateEvt) {
  1217.         if (dlg == (DialogPtr)event->message) OutlineDialogItem(dlg, 1);
  1218.         return(false);
  1219.     }
  1220.  
  1221.     if (event->what != keyDown) return(false);
  1222.  
  1223.     itemNum = 0;
  1224.  
  1225.     theChr = event->message   & charCodeMask;
  1226.     theMod = event->modifiers & keyCodeMask;
  1227.  
  1228.     if ((theChr == 0x0D) || (theChr == 0x03)) {        /* If return or enter... */
  1229.         if (!(theMod & (cmdKey + optionKey + controlKey))) itemNum = 1;
  1230.     }        
  1231.     else {
  1232.  
  1233.         GetDItem(dlg, theItem, &itemType, &itemHndl, &itemRect);
  1234.         GetIText(itemHndl, itemText);
  1235.  
  1236.         for (i = 1; i <= *itemText; i += 9) {
  1237.             equivChr = GetHexByte((char *)(itemText + i));
  1238.             modMask  = GetHexByte((char *)(itemText + i + 2)) << 8;
  1239.             modVal   = GetHexByte((char *)(itemText + i + 4)) << 8;
  1240.             itemNum  = GetHexByte((char *)(itemText + i + 6));
  1241.             if (theChr == equivChr)
  1242.                 if ((theMod & modMask) == modVal) break;
  1243.             itemNum = 0;
  1244.         }
  1245.     }
  1246.  
  1247.     if (itemNum) {
  1248.         itemNum += offset;
  1249.         GetDItem(dlg, itemNum, &itemType, &itemHndl, &itemRect);
  1250.         if ((* (ControlHandle) itemHndl)->contrlHilite == 255) return false;
  1251.         HiliteControl((ControlHandle)itemHndl, 1);
  1252.         tick = TickCount();
  1253.         while (TickCount() < tick + 10);
  1254.         HiliteControl((ControlHandle)itemHndl, 0);
  1255.         *item = itemNum;
  1256.         return(true);
  1257.     }
  1258.  
  1259.     return(false);
  1260. }
  1261.  
  1262. /* Return the value of a dialog item
  1263.  */
  1264. short GetDItemValue(DialogPtr dlgPtr, short itemNo)
  1265. {
  1266.     short    iKind;
  1267.     Handle    iHandle;
  1268.     Rect    iRect;
  1269.  
  1270.     GetDItem(dlgPtr, itemNo, &iKind, &iHandle, &iRect);
  1271.     return(GetCtlValue((ControlHandle)iHandle));
  1272. }
  1273.  
  1274. /* Custom dialog centering routine which returns a color dialog port and
  1275.  * uses CenterWindow (Utilities.c) to actually center the window.
  1276.  */
  1277. DialogPtr MyGetCenteredDialog(short id, DialogPtr storage, WindowPtr relatedWindow, WindowPtr behind)
  1278. {
  1279.     DialogTHndl        dlogResource;
  1280.     DialogPtr        dialog;
  1281.     Handle            ditl;
  1282.     Boolean            oldVis;
  1283.     char            hstate;
  1284.     OSErr            err;
  1285.     
  1286.     dialog = nil;
  1287.     if (dlogResource = (DialogTHndl)GetAppResource('DLOG', id, &err)) {
  1288.         hstate = LockHandleHigh((Handle)dlogResource);
  1289.         oldVis = (*dlogResource)->visible;
  1290.         (*dlogResource)->visible = false;
  1291.         ditl = GetAppResource('DITL', id, &err);
  1292.         DetachResource(ditl);
  1293.         if (gQDVersion) {
  1294.             if (dialog =
  1295.                     NewCDialog(storage, &(*dlogResource)->boundsRect,
  1296.                     (*dlogResource)->title, false, (*dlogResource)->procID,
  1297.                     behind, (*dlogResource)->goAwayFlag,
  1298.                     (*dlogResource)->refCon, ditl)) {
  1299.                 Rect sizeInfo = {0,0,0,0};
  1300.                 
  1301.                 CenterWindow(dialog, relatedWindow, sizeInfo);
  1302.                 if (oldVis) ShowWindow(dialog);
  1303.             }
  1304.         } else {
  1305.             if (dialog =
  1306.                     NewDialog(storage, &(*dlogResource)->boundsRect,
  1307.                     (*dlogResource)->title, false, (*dlogResource)->procID,
  1308.                     behind, (*dlogResource)->goAwayFlag,
  1309.                     (*dlogResource)->refCon, ditl)) {
  1310.                 Rect sizeInfo = {0,0,0,0};
  1311.                 
  1312.                 CenterWindow(dialog, relatedWindow, sizeInfo);
  1313.                 if (oldVis) ShowWindow(dialog);
  1314.             }
  1315.         }
  1316.         (*dlogResource)->visible = oldVis;
  1317.         HSetState((Handle)dlogResource, hstate);
  1318.     }
  1319.     return(dialog);
  1320. }
  1321.  
  1322. /* Copy a Pascal string
  1323.  */
  1324. void Pstrcpy(Str255 dest, ConstStr255Param src)
  1325. {
  1326.     register short i;
  1327.     
  1328.     for(i=0; i <= src[0]; i++) {
  1329.         dest[i] = src[i];
  1330.     }
  1331. }
  1332.  
  1333. /* Enable or disable a dialog item
  1334.  */
  1335. void AbleDItem(DialogPtr dlog, short item, short state)
  1336. {
  1337.     Handle iHandle;
  1338.     Rect iRect;
  1339.     short iType;
  1340.  
  1341.     GetDItem(dlog, item, &iType, &iHandle, &iRect);
  1342.      HiliteControl((ControlHandle) iHandle, state ? 0 : 255);
  1343. }
  1344.